﻿#include "FITKGraphObjectPredefinedFieldVelocity.h"

// VTK
#include <vtkPoints.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkProperty.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkArrowSource.h>
#include <vtkRenderer.h>
#include <vtkIntArray.h>
#include <vtkDoubleArray.h>
#include <vtkExtractSelection.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaPredefinedFieldVelocity.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

namespace Exchange
{
    FITKGraphObjectPredefinedFieldVelocity::FITKGraphObjectPredefinedFieldVelocity(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaPredefinedFieldVelocity* fieldData)
        : FITKGraphObjectPredefinedFieldBase(caseData, fieldData)
    {
        if (!fieldData)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the grid data.
        bool flag = generatePolyData(fieldData);
        if (!flag)
        {
            return;
        }

        // Generate tie actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();
    }

    FITKGraphObjectPredefinedFieldVelocity::~FITKGraphObjectPredefinedFieldVelocity()
    {
        // Delete source data.
        deleteVtkObj(m_arrowSource);

        // Delete the arrays.
        deleteVtkObj(m_normal);
        deleteVtkObj(m_scalar);
    }

    void FITKGraphObjectPredefinedFieldVelocity::init()
    {
        this->Superclass::init();

        // Create source data.
        m_arrowSource = vtkArrowSource::New();
        m_arrowSource->SetShaftRadius(0);
        m_arrowSource->SetTipLength(0.2);
        m_arrowSource->SetTipRadius(0.08);
        m_arrowSource->SetTipResolution(m_sourceResolution);

        // Create normal and scalar arrays.
        m_normal = vtkDoubleArray::New();
        m_normal->SetNumberOfComponents(3);

        m_scalar = vtkDoubleArray::New();
        m_scalar->SetNumberOfComponents(1);

        // Initialize the glyph size.
        m_glyphSize = 70.;
    }

    void FITKGraphObjectPredefinedFieldVelocity::initActors()
    {
        this->Superclass::initActors();

        // Get the field color .
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_velFieldColor, color3);

        // Initialize actor properties.
        m_fActor->GetProperty()->SetRepresentation(1);
        m_fActor->GetProperty()->SetColor(color3);
        m_fActor->GetProperty()->SetLineWidth(2);
    }

    void FITKGraphObjectPredefinedFieldVelocity::clearData()
    {
        this->Superclass::clearData();
        
        // Clear noraml and scalar.
        resetVtkObj(m_normal);
        resetVtkObj(m_scalar);
    }

    bool FITKGraphObjectPredefinedFieldVelocity::dataChanged()
    {
        Interface::FITKAbaPredefinedFieldVelocity* velField = dynamic_cast<Interface::FITKAbaPredefinedFieldVelocity*>(_dataObj);
        if (!velField)
        {
            return false;
        }

        // Get the values.
        double value[3]{ 0., 0., 0. };
        value[0] = velField->getV1();
        value[1] = velField->getV2();
        value[2] = velField->getV3();

        bool hasChanged = !qFuzzyCompare(m_value[0], value[0]) ||
            !qFuzzyCompare(m_value[1], value[1]) ||
            !qFuzzyCompare(m_value[2], value[2]);

        return hasChanged;
    }

    void FITKGraphObjectPredefinedFieldVelocity::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!getDataVisibility())
        {
            return;
        }

        // Get the field highlight color.
        double color3[3] = { 0 ,0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_velFieldHighlightColor, color3);
        }      
        
        // Set the highlight color for glyph.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }

        FITKGraphObject3D::highlight();
    }

    void FITKGraphObjectPredefinedFieldVelocity::disHighlight()
    {
        // Get the field default color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_velFieldColor, color3);

        // Set the default color for glyph.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }     

        FITKGraphObject3D::disHighlight();
    }

    bool FITKGraphObjectPredefinedFieldVelocity::generatePolyData(Interface::FITKAbaAbstractPredefinedField* fieldData)
    {
        Interface::FITKAbaPredefinedFieldVelocity* velField = dynamic_cast<Interface::FITKAbaPredefinedFieldVelocity*>(fieldData);
        if (!velField)
        {
            return false;
        }

        // Generate location grid.
        bool flag = this->Superclass::generatePolyData(velField);
        if (!flag)
        {
            return false;
        }

        // Save the values.
        m_value[0] = velField->getV1();
        m_value[1] = velField->getV2();
        m_value[2] = velField->getV3();

        // Add points for glyph.
        //@{
        // Get the model id array of location.
        for (int i = 0; i < 3; i++)
        {
            // Check if the value is 0.
            if (qFuzzyCompare(m_value[i], 0))
            {
                continue;
            }

            // The normal value.
            double normal[3] = { 0., 0., 0. };
            normal[i] = qAbs(m_value[i]) / m_value[i];

            for (int j = 0; j < m_gridLoc->GetNumberOfPoints(); j++)
            {
                int modelId = -1;
                if (m_modelIdArrayLoc)
                {
                    modelId = m_modelIdArrayLoc->GetValue(j);
                }

                // Add a point and a cell.
                //@{
                // Add point.
                int ptId = m_points->InsertNextPoint(m_gridLoc->GetPoint(j));

                // Add cell.
                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(ptId);
                m_ugrid->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

                // Add model id and normal.
                m_modelIdArray->InsertNextValue(modelId);
                //@}

                // Add normal and scalar.
                m_normal->InsertNextTuple3(normal[0], normal[1], normal[2]);
                m_scalar->InsertNextValue(1.);
            }
        }
        //@}

        return true;
    }

    void FITKGraphObjectPredefinedFieldVelocity::generateGraph()
    {
        if (!m_normal || !m_scalar || !m_ugrid)
        {
            return;
        }

        // Create actors.
        initActors();

        // Initialize the actor's input grid.
        m_ugrid->GetPointData()->SetScalars(m_scalar);
        m_ugrid->GetPointData()->SetNormals(m_normal);

        // Glyph.
        vtkGlyph3D* glyph = vtkGlyph3D::New();
        vtkDistanceToCamera* distanceToCamera = vtkDistanceToCamera::New();
        distanceToCamera->SetScreenSize(m_glyphSize);

        glyph->SetInputConnection(distanceToCamera->GetOutputPort());

        // Add the point array name need to be used.      
#if VTK_MAJOR_VERSION < 8
        glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
        glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, distanceToCamera->GetDistanceArrayName());
#endif

        glyph->SetScaling(true);
        glyph->SetScaleModeToScaleByScalar();
        glyph->SetVectorModeToUseNormal();

        // Set the soure data and input data.
        distanceToCamera->SetInputConnection(m_extractSelection->GetOutputPort());
        glyph->SetSourceConnection(m_arrowSource->GetOutputPort());

        // Add to data list.
        m_glyphList.push_back(glyph);
        m_distToCameraList.push_back(distanceToCamera);

        // Initialize the actor's input.
        m_fActor->setInputConnection(glyph->GetOutputPort());
    }

    void FITKGraphObjectPredefinedFieldVelocity::setFITKRender(Comp::FITKGraphRender* render)
    {
        if (!render)
        {
            return;
        }

        vtkRenderer* renderer = render->getRenderer();
        if (!renderer)
        {
            return;
        }

        // Set the renderer for calculating size.
        for (vtkDistanceToCamera* obj : m_distToCameraList)
        {
            if (obj)
            {
                obj->SetRenderer(renderer);
            }
        }

        // Save the render.
        this->Superclass::setFITKRender(render);
    }
}   // namespace Exchange